package com.fx.common;

import javafx.event.EventHandler;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.Region;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

public abstract class AbstractWindowController {

    private final Stage owner;
    private Parent root;
    private Scene scene;
    private Stage stage;
    private final boolean sizeToScene;
    private final double CLAMP_FACTOR = 0.9;

    private final EventHandler<WindowEvent> closeRequestHandler = e -> {
        onCloseRequest(e);
        e.consume();
    };

    public AbstractWindowController(){
        this(null,true);
    }

    public AbstractWindowController(Stage owner){
        this(owner,true);
    }

    public AbstractWindowController(Stage owner,boolean sizeToScene){
        this.owner = owner;
        this.sizeToScene = sizeToScene;
    }

    public Parent getRoot(){
        if(root == null){
            makeRoot();
        }
        return root;
    }

    public Scene getScene(){
        if(scene == null){
            scene = new Scene(getRoot());
            controllerDidCreateScene();
        }
        return scene;
    }

    public Stage getStage(){
        if(stage == null){
            stage = new Stage();
            stage.initOwner(this.owner);
            stage.setOnCloseRequest(closeRequestHandler);
            stage.setScene(getScene());
            clampWindow();
            if(sizeToScene){
                stage.sizeToScene();
            }
            if(owner != null){
                stage.getIcons().addAll(owner.getIcons());
            }
            controllerDidCreateScene();
        }
        return stage;
    }

    public void openWindow(){
        getStage().show();
        getStage().toFront();
    }

    public void closeWindow(){
        getStage().close();
    }

    protected final void setRoot(Parent root){
        assert  root != null;
        this.root = root;
    }

    protected abstract void makeRoot();

    public abstract void onCloseRequest(WindowEvent e);

    protected void controllerDidCreateScene() {
        assert getRoot() != null;
        assert getRoot().getScene() != null;
        assert getRoot().getScene().getWindow() == null;
    }

    protected void controllerDidCreateStage() {
        assert getRoot() != null;
        assert getRoot().getScene() != null;
        assert getRoot().getScene().getWindow() != null;
    }

    private void clampWindow() {
        if (getRoot() instanceof Region) {
            Rectangle2D vBounds = Screen.getPrimary().getVisualBounds();
            double primaryScreenHeight = vBounds.getHeight();
            double primaryScreenWidth = vBounds.getWidth();
            double currentHeight = getRoot().prefHeight(-1);
            double currentWidth = getRoot().prefWidth(-1);

            if (currentHeight > primaryScreenHeight) {
                double newHeight = primaryScreenHeight * CLAMP_FACTOR;
                //            System.out.println("Clamp: new height is " + newHeight);
                assert getRoot() instanceof Region;
                ((Region)getRoot()).setPrefHeight(newHeight);
            }

            if (currentWidth > primaryScreenWidth) {
                double newWidth = primaryScreenWidth * CLAMP_FACTOR;
                //            System.out.println("Clamp: new width is " + newWidth);
                assert getRoot() instanceof Region;
                ((Region)getRoot()).setPrefWidth(newWidth);
            }
        }
    }
}
